package com.example.demo35delegatingpasswordencoder.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 *  使用 UsernamePasswordAuthenticationFilter 进行登录的 Provider 为 DaoAuthenticationProvider，
 *  DaoAuthenticationProvider 的 PasswordEncoder 默认为 DelegatingPasswordEncoder，
 *  通过 PasswordEncoderFactories.createDelegatingPasswordEncoder() 创建
 *      1. 在 PasswordEncoderFactories 中，首先构建了一个 encoders，然后给所有的编码方式都取了一个名字，
 *          再把名字做 key，编码方式做 value，统统存入 encoders 中。
 *      2. 最后返回了一个 DelegatingPasswordEncoder 实例，同时传入默认的 encodingId 就是 bcrypt，
 *          以及 encoders 实例，DelegatingPasswordEncoder 看名字应该是一个代理对象。
 *
 *  1. DelegatingPasswordEncoder 也是实现了 PasswordEncoder 接口，所以它里边的核心方法也是两个：encode 方法用来对密码进行编码，matches 方法用来校验密码。
 *  2. 在 DelegatingPasswordEncoder 的构造方法中，通过 通过传入的两个参数 encodingId 和 encoders ，获取到默认的编码器赋值给 passwordEncoderForEncode，默认的编码器实际上就是 BCryptPasswordEncoder。
 *  3. 在 encode 方法中对密码进行编码，但是编码的方式加了前缀，前缀是 {编码器名称} ，例如如果你使用 BCryptPasswordEncoder 进行编码，那么生成的密码就类似 {bcrypt}$2a$10$oE39aG10kB/rFu2vQeCJTu/V/v4n6DRR0f8WyXRiAYvBpmadoOBE.。这样有什么用呢？每种密码加密之后，都会加上一个前缀，这样看到前缀，就知道该密文是使用哪个编码器生成的了。
 *  4. 最后 matches 方法的逻辑就很清晰了，先从密文中提取出来前缀，再根据前缀找到对应的 PasswordEncoder，然后再调用 PasswordEncoder 的 matches 方法进行密码比对。
 *  5. 如果根据提取出来的前缀，找不到对应的 PasswordEncoder，那么就会调用 UnmappedIdPasswordEncoder#matches 方法，进行密码比对，该方法实际上并不会进行密码比对，而是直接抛出异常。
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("wangnian").password("{MD5}{mCZ5wdVMcjkLBogctISIVJl6QVIsTIch7RwOh7J8fwU=}a16e01f76601e942ef942ca3e80dcaf8").roles("admin").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("zhangsan").password("{bcrypt}$2a$10$QLEhIQi.cMLtazDiKfKepu2jgTpxkacZ9jBJRMNP7wWkj6FSo/3.e").roles("user").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("lisi").password("{noop}123").roles("user").build());
        return inMemoryUserDetailsManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/user/**").hasAnyRole("admin","user")
                .anyRequest().authenticated()
                .and().formLogin()
                .and().csrf().disable();
    }
}
